CloudFormation StackSetsのインポートが失敗するときの対処法
こんにちは。たかやまです。
CloudFormation StackSetsでは既存スタックをStackSetsにインポートする機能を提供しています。
インポート処理をした場合にエラーになるパターンがいくつかあったので、その対処法をまとめます。
さきにまとめ
- StackSetsとインポート対象スタックのテンプレートに差分がある場合
- StackSetsのスタックインスタンスから失敗したインポート対象スタックインスタンスを削除する
- インポート対象スタックのテンプレートをStackSetsのテンプレートに合わせる
- インポート対象スタックを再度インポートする
- インポート対象スタックがすでにスタックインスタンスとして存在する場合
- StackSetsのスタックインスタンスからインポート対象スタックインスタンスを削除する
- インポート対象スタックを再度インポートする
- NoEchoを含むテンプレートはChangeSetで差分が検知されるため、インポート処理に失敗する
やってみる
前提として以下のテンプレートを展開するStackSetsを作成します。
AWSTemplateFormatVersion: "2010-09-09"
Resources:
S3Bucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Sub "stackset-${AWS::AccountId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Outputs:
BucketName:
Description: "Name of the created S3 bucket"
Value: !Ref S3Bucket
インポートは以下の import-stacks-to-stack-set コマンドを使います。
aws cloudformation import-stacks-to-stack-set \
--stack-set <スタックセット名> \
--stack-ids "<既存アカウントスタックId 1>" "<既存アカウントスタックId 2>" ... \
--organizational-unit-ids "<インポート対象先OUのUnitId>"
StackSetsとインポート対象スタックのテンプレートに差分がある場合
import-stacks-to-stack-set を使ってメンバーアカウントのスタックをインポートしてみます。
すると、以下のようなエラーが発生しました
ChangeSet: `StackImport-4ae0b90190f3565d9aa720f63e29bd98` contains changes: `[{Type: Resource,ResourceChange: {Action: Modify,LogicalResourceId: S3Bucket,PhysicalResourceId: stackset-73XXXXXXXX81,ResourceType: AWS::S3::Bucket,Replacement: False,Scope: [Properties],Details: [{Target: {Attribute: Properties,Name: Tags,RequiresRecreation: Never,},Evaluation: Static,ChangeSource: DirectModification,}],},ChangeAnalysisResults: []}]`
こちらはStackSetsのテンプレート内容とインポート対象スタックのテンプレート内容に差分がある場合に発生します。
エラー内容を見ていただくと、contains changes
に差分内容が含まれていることがわかります。
[
{
Type: Resource,
ResourceChange:
{
Action: Modify,
LogicalResourceId: S3Bucket,
PhysicalResourceId: stackset-73XXXXXXXX81,
ResourceType: AWS::S3::Bucket,
Replacement: False,
Scope: [Properties],
Details:
[
{
Target: { Attribute: Properties, Name: Tags, RequiresRecreation: Never },
Evaluation: Static,
ChangeSource: DirectModification,
},
],
},
ChangeAnalysisResults: [],
},
]
対処法
こちらのエラーを解消するには、以下の手順を実施する必要があります。
- StackSetsのスタックインスタンスから失敗したインポート対象スタックインスタンスを削除する
- インポート対象スタックのテンプレートをStackSetsのテンプレートに合わせる
- インポート対象スタックを再度インポートする
参考 : AWS CloudFormation StackSets のトラブルシューティング - AWS CloudFormation
では実際に対処していきたいと思います。
1. StackSetsのスタックインスタンスから失敗したインポート対象スタックインスタンスを削除する
以下のコマンドでStackSetsのスタックインスタンスから失敗したインポート対象スタックインスタンスを削除します。
aws cloudformation delete-stack-instances \
--stack-set <スタックセット名> \
--deployment-targets 'OrganizationalUnitIds=["<インポート対象先OUのUnitId>"],Accounts=["<失敗したインポート対象アカウント>"],AccountFilterType=INTERSECTION' \
--regions <リージョン> \
--retain-stacks
実行コマンド
aws cloudformation delete-stack-instances \
--stack-set example-stacksets \
--deployment-targets 'OrganizationalUnitIds=["ou-0orw-bvsrcrpk"],Accounts=["73XXXXXXXX81"],AccountFilterType=INTERSECTION' \
--regions ap-northeast-1 \
--retain-stacks
削除オペレーションが完了すると、スタックインスタンスか失敗しているスタックインスタンスが削除されていることが確認できます。
2. インポート対象スタックのテンプレートをStackSetsのテンプレートに合わせる
今回インポート対象スタックのテンプレートは以下のようになっています。
Tags
の値が追加されており、StackSetsのテンプレートと差分があるためエラーが発生しています。
AWSTemplateFormatVersion: "2010-09-09"
Resources:
S3Bucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Sub "stackset-${AWS::AccountId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: "Example"
Value: "Dummy"
Outputs:
BucketName:
Description: "Name of the created S3 bucket"
Value: !Ref S3Bucket
こちらの差分を解消するためにインポート対象スタックが存在するアカウントでテンプレートを更新します。
StackSetsで展開されるCloudFormation名はStackSet-<スタックセット名>-<ランダムな文字列>
がついています。
3. インポート対象スタックを再度インポートする
インポート対象スタックのテンプレートをStackSetsのテンプレートに合わせた後、再度インポート処理を行います。
今度は問題なく、インポート対象スタックがインポートされることを確認できます。
小ネタ
エラーメッセージを見ていただくと分かる通り、差分のチェックはCloudFormationのChangeSetを使って行われています。
実際にインポート先アカウントを見てみると以下のようなChangeSetが作成されていることが確認できます。
この差分が発生している場合にはStackSetsはインポート処理を失敗させるようです。
インポートが成功している場合には、以下のようにChangeSetに差分が存在しないことを表すエラーになります。
The submitted information didn't contain changes. Submit different information to create a change set.
インポート対象スタックがすでにスタックインスタンスとして存在する場合
以下のようにすでにスタックインスタンスが存在する状態(SUCCEEDED
/FAILED
問わず)でインポート処理を行います。
すでにスタックインスタンスが存在する状態でのインポート処理は以下のようにFAILED
となります。
ただ、このときエラー内容は特に確認することができません。
対処法
結論からいうと、この場合には一度スタックインスタンスを削除してからインポート処理を行う必要があります。
なので、テンプレート差分修正の手順で行ったようにスタックインスタンスを削除して再度インポート処理を実施してください。
- StackSetsのスタックインスタンスからインポート対象スタックインスタンスを削除する
- インポート対象スタックを再度インポートする
もし、ここで再度エラーが発生した場合にはテンプレート側の問題が考えられますので、テンプレートの修正を行ってください。
(余談)テンプレートにNoEchoが含まれる場合
最後にStackSetsでサポートされていないNoEcho
を含むテンプレートのインポート処理をためしてみたいと思います。
NoEcho プロパティは StackSet インポートにサポートされていません。NoEcho を含むスタックは、StackSet のインポートを介して新しいスタックセットにインポートされません。
AWS CloudFormation StackSets へのスタックのインポート - AWS CloudFormation
テンプレートは以下のようにTags
のValueでNoEchoの値を使うようなものを用意しています
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
BucketTagValue:
Type: String
Description: "Name of the S3 bucket"
NoEcho: true
Resources:
S3Bucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Sub "stackset-${AWS::AccountId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: "Example"
Value: !Ref BucketTagValue
Outputs:
BucketName:
Description: "Name of the created S3 bucket"
Value: !Ref S3Bucket
こちらでインポート処理を行うと以下のようなエラーが発生しました。
「StackSetsとインポート対象スタックのテンプレートに差分がある場合」のパターンと同様にテンプレート内容に差分を検知してインポートに失敗しているようです。
インポート対象アカウントのスタックを確認すると以下のようなChangeSetが作成されていることが確認できます。
"Value": "****"
が検知されるようにNoEchoの値がChangeSetとして検知されるため、StackSet インポートではサポートされないようですね。
最後に
今回はCloudFormation StackSetsのインポート処理が失敗する場合の対処法についてまとめました。
エラーメッセージが出てくれるものは対処法が明確ですが、「インポート対象スタックがすでにスタックインスタンスとして存在する場合」のようにエラー内容がなくハマってしまったで今回の記事を書いてみました。
こちらの内容がどなたかのお役に立てれば幸いです。
以上、たかやま(@nyan_kotaroo)でした。